<!DOCTYPE HTML>
<html lang="en-US">
<head>
  <title>Albert - Extending Albert</title>
  <meta charset="UTF-8">
  <link rel="icon" type="image/x-icon" href="/favicon.ico">
  <link rel="alternate" type="application/rss+xml" title="Albert RSS" href="/feed.xml">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
  <link rel="stylesheet" href="https://albertlauncher.github.io/css/main.css">
</head>

<body>
    <main class="wrapper">
      <nav class="site-header">
    <div class="container">
        <a class="title" href="https://albertlauncher.github.io">
            <img class="icon" src="https://albertlauncher.github.io/img/albert.svg" alt="Albert" title="Albert">
            <span class="title">Albert</span>
        </a>
        <nav class="navigation float-right">
            <ul>
    <li class="">
        <a href="/">Home</a>
    </li>
    <li class="current">
        <a href="/docs/">Docs</a>
    </li>
    <li class="">
        <a href="/news/">News</a>
    </li>
    <li class="">
        <a href="/help/">Help</a>
    </li>
    <li>
        <a href="https://github.com/albertlauncher/albert.git"><span class="hide-on-mobiles">View on </span>GitHub</a>
    </li>
</ul>

        </nav>
    </div>
</nav>

      <section class="container docs">
    <article>
        <div class="row">
            <div class="column column-20">
              <aside id="doc_nav">
  
    <h5>User guide</h5>
    <ul>

  
  
  <li class=""><a href="/docs/installing/">Installing Albert</a></li>

  
  
  <li class=""><a href="/docs/using/">Using Albert</a></li>

  
  
  <li class="current"><a href="/docs/extending/">Extending Albert</a></li>

  
  
  <li class=""><a href="/docs/extensions/">Extensions</a></li>

  
  
  <li class=""><a href="/docs/faq/">Troubleshooting</a></li>

</ul>

  
    <h5>Meta</h5>
    <ul>

  
  
  <li class=""><a href="/docs/contributing/">Contributing</a></li>

  
  
  <li class=""><a href="/docs/donation/">Donating</a></li>

  
  
  <li class=""><a href="/docs/changelog/">Changelog</a></li>

</ul>

  
</aside>

            </div>
            <div class="column column-80">
                <div class="float-right">
                    <a href="https://github.com/albertlauncher/documentation/edit/master/src/_docs/extending.md">Improve this page</a>
                </div>
                <h1>Extending Albert</h1>
                <p>Albert has a flexible extension system, which provides users and developers with the ability to extend the functionality by creating <em>plugins</em>. These plugins can be used to provide several kinds of functionality by implementing particular interfaces. An instance of such an interface is called an <em>extension</em>. A plugin can provide several extensions.</p>

<p>The native and primary way is to use C++/Qt to write a QPlugin. This gives you the performance of C++, the full set of interfaces to implement, access to several utility classes and direct access to the application and Qt framework including the event loop, which allows asynchronous operations. C++ knowledge is not that widespread therefore there are extensions that make albert scriptable. The primary way to script albert is the <a href="/docs/extensions/python/">python extension</a>. It supports a good fraction of the internal API, is in memory and pretty fast. A more flexible but less performant way to extend Albert are <a href="/docs/extensions/external/">external extensions</a>. Since they are basically CGI executables you can use <em>any</em> language that can be used to build executable files.</p>

<p>This article is about the native extension of the albert launcher.</p>

<h3 id="overview">Overview</h3>

<p>A native albert extension is a <a href="http://doc.qt.io/qt-5/plugins-howto.html#the-low-level-api-extending-qt-applications.">Qt Plugin</a> which is nothing else but a special shared library. A plugin has to have the correct interface id (IID) and of course to implement this interfaces to be loaded, e.g. <code class="highlighter-rouge">Core::Extension</code> or <code class="highlighter-rouge">Core::Frontend</code>. The best way to to get an overview is to read the <a href="https://github.com/albertlauncher/albert/tree/master/include/albert">core library interface</a> classes. The headers comments and the other plugins especially the <a href="https://github.com/albertlauncher/plugins/tree/master/templateExtension">template extension</a> should get you started.</p>

<p>The internal API is still not final yet. If you want to write a plugin check the other extensions. There are some caveats and requirements you should know:</p>

<ul>
  <li>Qt Plugin needs a metadata file</li>
  <li>The metadata needs a unique id to be loaded</li>
  <li>The metadata file name must match the name in the <code class="highlighter-rouge">Q_PLUGIN_METADATA</code> macro</li>
  <li>The interface id defined in <code class="highlighter-rouge">Q_PLUGIN_METADATA</code> must match the current one defined in the interface headers for the plugin to be loaded.</li>
</ul>

<h3 id="getting-started">Getting started</h3>

<p>The best way to get started is to copy the <a href="https://github.com/albertlauncher/plugins/tree/master/templateExtension">template extension</a> and adjust the contents.</p>

<p>To keep the code readable there are some conventions that are not strictly necessary, but the intention is to unify the filenames of the plugins.The main class of the extension is called <code class="highlighter-rouge">Extension</code> and if the extension returns a configuration widget the class shall be called <code class="highlighter-rouge">ConfigWidget</code>. The metadata file is called <code class="highlighter-rouge">metadata.json</code>. This would implicitly lead to naming conflicts, therefor all classes of an extensions live in a dedicated namespace having the name of the extension. In bullets:</p>

<ul>
  <li>Copy the template extension.</li>
  <li>Adjust the values contents in <code class="highlighter-rouge">metadata.json</code> and project name in <code class="highlighter-rouge">CmakeLists.txt</code>.</li>
  <li>Rename the namespace. Remember to define the namespace in the <code class="highlighter-rouge">*.ui</code> files, too.</li>
  <li>Make sure to have checked all core library headers .</li>
  <li>Implement your extension.</li>
</ul>

<p>Contact us if you need <a href="/help/">help</a>.</p>

<h3 id="extension-plugins">Extension plugins</h3>

<p>Implement the <code class="highlighter-rouge">Core::Extension</code> and <code class="highlighter-rouge">Core::QueryHandler</code> interface. Especially <code class="highlighter-rouge">Core::Extension</code> is not final yet. But this should not be a problem. Since changes would need you to just change a few lines of code.</p>

<p><code class="highlighter-rouge">Core::QueryHandler</code> has several functions that will be called on special events. Most important is the virtual function <code class="highlighter-rouge">handleQuery(Query)</code> this function will be called when the user types in his queries.</p>

<p>The <code class="highlighter-rouge">Core::Query</code> object contains all necessary information and accepts objects of abstract type <code class="highlighter-rouge">Core::Item</code>. Subclass it or use <code class="highlighter-rouge">Core::StandardItem</code>. The items interface has a getter for actions of abstract type <code class="highlighter-rouge">Core::Action</code>. Again subclass it or use <code class="highlighter-rouge">Core::StandardAction</code>. Furter there is the <code class="highlighter-rouge">Core::IndexableItem</code> interface with its standard implementation <code class="highlighter-rouge">Core::StandardIndexItem</code>. These items are for the use with the utility class <code class="highlighter-rouge">Core::OfflineIndex</code> which does basic offline indexing and searching for you.</p>

<p>To get a detailed description of the interfaces read the header files of the core library interface classes.</p>

<h3 id="frontend-plugins">Frontend plugins</h3>

<p>Implement the <code class="highlighter-rouge">Core::Frontend</code> interface. Implementing a frontend is a cumbersome process with tons of caveats. I will rather not write a documentation on it. <a href="/help/">Contact</a> us directly.</p>

<h3 id="the-plugin-metadata">The plugin metadata</h3>

<p>The plugin metadata is a mandatory file that is needed to compile the plugin. Its content is <em>JSON</em> formatted and its name has to be equal to the the one specified in the <code class="highlighter-rouge">Q_PLUGIN_METADATA</code> in the extensions main class. The convention is to call it <code class="highlighter-rouge">metadata.json</code>. Its fields give the application information about the plugin without having to load the plugin.</p>

<p>Currently the plugin specification has the following keys:</p>
<ul>
  <li><code class="highlighter-rouge">id</code> is the unique identifier of the app. A plugin will not be loaded if its id has been registered already by an other plugin.</li>
  <li><code class="highlighter-rouge">name</code> is the pretty printed name of the plugin.</li>
  <li><code class="highlighter-rouge">version</code> is, well, the version of the plugin.</li>
  <li><code class="highlighter-rouge">author</code> name of the developer of this plugin.</li>
  <li><code class="highlighter-rouge">dependencies</code> is an array of dependencies of this plugin. These dependencies are mandatory for the plugin but optional for the application. The user is responsible to install them.</li>
</ul>

<p>A plugin specification could look like the following:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w">              </span><span class="s2">"org.albert.extension.bookmarks"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w">            </span><span class="s2">"Bookmarks"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w">         </span><span class="s2">"1.1"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"author"</span><span class="w"> </span><span class="p">:</span><span class="w">          </span><span class="s2">"Manuel Schneider"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"dependencies"</span><span class="w"> </span><span class="p">:</span><span class="w">    </span><span class="p">[]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

            </div>
        </div>
    </article>
    </div>
</section>

      
      <script>
  var anchorForId = function (id) {
    var anchor = document.createElement("a");
    anchor.className = "header-link";
    anchor.href      = "#" + id;
    anchor.innerHTML = "<i class=\"fa fa-link\"></i>";
    anchor.title = "Permalink";
    return anchor;
  };

  var linkifyAnchors = function (level, containingElement) {
    var headers = containingElement.getElementsByTagName("h" + level);
    for (var h = 0; h < headers.length; h++) {
      var header = headers[h];

      if (typeof header.id !== "undefined" && header.id !== "") {
        header.appendChild(anchorForId(header.id));
      }
    }
  };

  document.onreadystatechange = function () {
    if (this.readyState === "complete") {
      var contentBlock = document.getElementsByClassName("docs")[0] || document.getElementsByClassName("news")[0];
      if (!contentBlock) {
        return;
      }
      for (var level = 1; level <= 6; level++) {
        linkifyAnchors(level, contentBlock);
      }
    }
  };

  var topEdge
  document.onscroll = function () {
    if (topEdge === undefined)
      topEdge = document.getElementById("doc_nav").getBoundingClientRect().top+window.pageYOffset-20;

    if ( window.pageYOffset > topEdge)
      document.getElementById("doc_nav").classList.add("stuck");
    else
      document.getElementById("doc_nav").classList.remove("stuck");
  };



</script>

    </main>
</body>
</html>
